

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>


#include "evalsoc.h"
#include "dbg_cli.h"


/*
 * ascii constants
 */

#define	SOH			0x01
#define	STX			0x02
#define	EOT			0x04
#define	ENQ			0x05
#define	ACK			0x06
#define	ASCII_LF	0x0a
#define	ASCII_CR	0x0d
#define	ASCII_XON	0x11
#define	ASCII_XOFF	0x13
#define	ASCII_CAN	0x18
#define	NAK			0x15



/*
 * zmodem constants
 */
#define	ZPAD		0x2a		/* pad character; begins frames */
#define	ZDLE		0x18		/* ctrl-x zmodem escape */
#define	ZDLEE		0x58		/* escaped ZDLE */

#define	ZBIN		0x41		/* binary frame indicator (CRC16) */
#define	ZHEX		0x42		/* hex frame indicator */
#define	ZBIN32		0x43		/* binary frame indicator (CRC32) */
#define	ZBINR32		0x44		/* run length encoded binary frame (CRC32) */



/*
 * zmodem frame types
 */
#define	ZRQINIT		0x00		/* request receive init (s->r) */
#define	ZRINIT		0x01		/* receive init (r->s) */
#define	ZSINIT		0x02		/* send init sequence (optional) (s->r) */
#define	ZACK		0x03		/* ack to ZRQINIT ZRINIT or ZSINIT (s<->r) */
#define	ZFILE		0x04		/* file name (s->r) */
#define	ZSKIP		0x05		/* skip this file (r->s) */
#define	ZNAK		0x06		/* last packet was corrupted (?) */
#define	ZABORT		0x07		/* abort batch transfers (?) */
#define	ZFIN		0x08		/* finish session (s<->r) */
#define	ZRPOS		0x09		/* resume data transmission here (r->s) */
#define	ZDATA		0x0a		/* data packet(s) follow (s->r) */
#define	ZEOF		0x0b		/* end of file reached (s->r) */
#define	ZFERR		0x0c		/* fatal read or write error detected (?) */
#define	ZCRC		0x0d		/* request for file CRC and response (?) */
#define	ZCHALLENGE	0x0e		/* security challenge (r->s) */
#define	ZCOMPL		0x0f		/* request is complete (?) */	
#define	ZCAN		0x10		/* pseudo frame; other end cancelled session with 5* CAN */
#define	ZFREECNT	0x11		/* request free bytes on file system (s->r) */
#define	ZCOMMAND	0x12		/* issue command (s->r) */
#define	ZSTDERR		0x13		/* output data to stderr (??) */



/*
 * ZDLE sequences
 */
#define	ZCRCE		0x68		/* CRC next, frame ends, header packet follows */
#define	ZCRCG		0x69		/* CRC next, frame continues nonstop */
#define	ZCRCQ		0x6a		/* CRC next, frame continuous, ZACK expected */
#define	ZCRCW		0x6b		/* CRC next, frame ends,       ZACK expected */
#define	ZRUB0		0x6c		/* translate to rubout 0x7f */
#define	ZRUB1		0x6d		/* translate to rubout 0xff */



/*
32 位 time CSR 寄存器, 用来计算 时间差值.
*/
static inline uint32_t __get_mtime( void )
{
	return (uint32_t)__RV_CSR_READ(CSR_TIME);
}



/*
@brief
	尽量读一个字符, 如果 ticks 时间内没有, 返回超时.
@param
	pdat, 容纳读输入的字符数据.
	ticks, 超时时间, us 时间单位.
@retval
	0 : 在规定时间内, 读输入一个字符数据.
	other :  time out, 没有发现数据.
*/
static int drv_rx_raw( uint8_t * pdat, uint32_t tmous )
{
	dw_uart_regs_t * puart = (dw_uart_regs_t *)UART2_BASE;
	uint32_t tlast;
	uint32_t tcurr;

	tlast = __get_mtime();
	
	/* todo : convert us to freq */
	tmous = tmous * 25;

    while ( (puart->LSR & 0x1) == 0 ) {
        __NOP();

        tcurr = __get_mtime();
        if ( (tcurr - tlast) >= tmous )  {
        	return 111;
        }
    }
    
    /**/
    *pdat = (uint8_t)puart->RBR;
    return 0;
}


static int drv_rx_purge( void )
{
	dw_uart_regs_t * puart = (dw_uart_regs_t *)UART2_BASE;
	uint8_t temp;

    while ( (puart->LSR & 0x1) != 0 ) {
        temp = (uint8_t)puart->RBR;
    }

	return temp;
}


/*
此函数中,  会进行字符转义处理.
所以收到 ZDLE 之后, 会解释下一个字符.
如果下一个字符是 ZCRCE, ZCECG, ZCRCQ, ZCRCW.
那么通过 iret 返回 +1 来表达.

@retval
  < 0 : 接收超时?
	0 : 正常字符, 没有转义的, 或者转义正常的.
	1 : 不符合转义的规则的, 所以没有进行 异或 0x40, 把 ZDLE 之后的原始字符返回了.
*/
static int drv_recv_escap( uint8_t * pdat, uint32_t tmous )
{
	int iret;
	uint8_t tch;

	/**/
preretry:
	iret = drv_rx_raw( &tch, tmous );
	if ( iret != 0 )  {
		return -100;
	}

	/* 消除控制字符. */
	if ( (tch == ASCII_XON) || (tch == ASCII_XOFF) )  {
		goto preretry;
	}

	if ( tch != ZDLE )  {
		*pdat = tch;
		return 0;
	}

	/**/
	iret = drv_rx_raw( &tch, tmous );
	if ( iret != 0 )  {
		return -102;
	}

	switch ( tch )  {
	
	case 0x50:	/* DLE */
	case 0xD0:	/* DLE | 0x80 */
	case 0x51:	/* XON */
	case 0xD1:	/* XON | 0x80 */
	case 0x53:	/* XOFF */
	case 0xD3:	/* XOFF | 0x80 */
	case 0x58:	/* ZDLE */
		*pdat = tch ^ 0x40;
		return 0;

	default:
		*pdat = tch;
		return 1;
	}
}



static void drv_tx_raw( uint8_t data )
{
	dw_uart_regs_t * puart = (dw_uart_regs_t *)UART2_BASE;

    while ( (puart->LSR & 0x20) == 0 ) {
        __NOP();
    }
    
    /**/
    puart->THR = (uint32_t)data;
    return;
}


static void drv_tx_escap( uint8_t data )
{
	switch ( data ) {
	case ZDLE:
    case 0x10:	/* DLE */
    case 0x90:	/* DLE | 0x80 */
    case 0x11:	/* XON */
    case 0x91:	/* XON | 0x80 */
    case 0x13:	/* XOFF */
    case 0x93:	/* XOFF | 0x80 */
    	drv_tx_raw( ZDLE );
    	drv_tx_raw( data ^ 0x40 );
    	break;

	default:
		drv_tx_raw( data );
		break;
	}
	
	return;
}


static void drv_tx_hex( uint8_t data )
{
	uint8_t temp;

	temp = data >> 4;
	if ( temp >= 10 )  {
		temp = (temp - 10) + 'a';
	} else {
		temp = temp + '0';
	}
	
	drv_tx_raw( temp );

	temp = data & 0x0F;
	if ( temp >= 10 )  {
		temp = (temp - 10) + 'a';
	} else {
		temp = temp + '0';
	}
	
	drv_tx_raw( temp );
	return;
}




/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
uint16_t crc16tab[256] = {
    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
};


static uint16_t update_crc16( uint8_t data,  uint16_t crc )
{
	return crc16tab[((crc >> 8) & 0xFF) ^ data] ^ (crc << 8);
}



/*

Figure 1.  Order of Bytes in	Header

TYPE:  frame type
  F0: Flags least significant byte
  P0: file Position least significant
  P3: file Position most significant

TYPE    F3 F2 F1 F0
-------------------
TYPE    P0 P1 P2 P3

*/




/*
@brief
	自动计算 crc, 处理字符转义.
@param
	type, zmodem frame types, 见上面宏定义.
	args, 4 字节, 或者是 flag, 或者是 postion.
@retval
*/
int send_bin16_frame( uint8_t type, uint32_t args )
{
	uint16_t crc;
	uint8_t temp;

	/* ZPAD, ZPAD, ZDLE, ZBIN */
	drv_tx_raw( ZPAD );
	drv_tx_raw( ZPAD );
	drv_tx_raw( ZDLE );
	drv_tx_raw( ZBIN );

	/* crc */
	drv_tx_raw( type );
	crc = type;
	
	for ( int i=0; i<4; i++ )  {

		temp = args & 0xFF;
		drv_tx_escap( temp );

		crc = update_crc16( temp, crc );
		args = args >> 8;
	}

	crc = update_crc16( 0, crc );
	crc = update_crc16( 0, crc );

	/* send crc */
	drv_tx_escap( crc >> 8 );
	drv_tx_escap( crc & 0xFF );
	return 0;
}


int send_hex_frame( uint8_t type, uint32_t args )
{
	uint16_t crc;
	uint8_t temp;

	/* ZPAD, ZPAD, ZDLE, ZHEX */
	drv_tx_raw( ZPAD );
	drv_tx_raw( ZPAD );
	drv_tx_raw( ZDLE );
	drv_tx_raw( ZHEX );

	/* crc */
	drv_tx_hex( type );
	crc = update_crc16( type, 0 );
	
	for ( int i=0; i<4; i++ )  {

		temp = args & 0xFF;
		drv_tx_hex( temp );
		
		crc = update_crc16( temp, crc );
		args = args >> 8;
	}

	// crc = update_crc16( 0, crc );
	// crc = update_crc16( 0, crc );

	/* send crc */
	drv_tx_hex( crc >> 8 );
	drv_tx_hex( crc & 0xFF );

	/* 有些软件把 0x0D, 0x0A,  转换为 0x8D, 0x8A.
	 * 不清楚因为哪些历史原因??
	 */
	drv_tx_raw( ASCII_CR );
	drv_tx_raw( ASCII_LF );

	/**/
	if ( (type != ZACK) && ( type != ZFIN) )  {
		drv_tx_raw( ASCII_XON );	
	}
	
	return 0;
}



/*
@brief
@param
	stype : ZDLE sequences
@retval
*/
int send_data_subpacket( uint8_t stype, int tlen, uint8_t * pdat )
{
	uint16_t crc;
	
	/**/
	crc = 0;
	for ( int i=0; i<tlen; i++ )  {
		drv_tx_escap( pdat[i] );
		crc = update_crc16( pdat[i], crc );	
	}

	/* subpacket framend. */
	crc = update_crc16( stype, crc );	
	drv_tx_raw( ZDLE );
	drv_tx_raw( stype );

	/* crc */
	crc = update_crc16( 0, crc );
	crc = update_crc16( 0, crc );
	drv_tx_escap( crc >> 8 );
	drv_tx_escap( crc & 0xFF );

	/**/
	if ( stype == ZCRCW ) {
		drv_tx_raw( ASCII_XON );
	}

	return 0;
}


/*
BIN, CRC-16
*/
int recv_bin_frame( uint32_t chtmo, uint8_t * ptype, uint32_t * pargs )
{
	int iret;
	uint16_t crc;
	uint32_t temp;
	uint8_t tary[16];

	/**/
	crc = 0;
	for ( int i=0; i<7; i++ )  {
		
		iret = drv_recv_escap( &tary[i], chtmo );
		if ( iret != 0 )  {
			return 1344;
		}

		/**/
		crc = update_crc16( tary[i], crc );
	}
	
	/* check crc */
	if ( crc != 0 )  {
		return 1355;
	}

	/**/
	*ptype = tary[0];
	temp = tary[4];
	temp = (temp << 8) | tary[3];
	temp = (temp << 8) | tary[2];
	temp = (temp << 8) | tary[1];
	*pargs = temp;
	return 0;
}



/*
HEX, CRC-16
*/
int recv_hex_frame( uint32_t chtmo, uint8_t * ptype, uint32_t * pargs )
{
	int iret;
	uint16_t crc;
	uint32_t temp;
	uint8_t tch;
	uint8_t tary[16];

	/* hex digital to frame : Type(2), Args(8), CRC16(4) */
	for ( int i=0; i<14; i++ )  {

		iret = drv_rx_raw( &tch, chtmo );
		if ( iret != 0 )  {
			/* check time out */
			return 1233;	
		}

		/* hex range check. */
		if ( (tch >= 0x30) && (tch <= 0x39) )  {
			tary[i]	= tch - 0x30;
		} else if ( (tch >= 0x61) && (tch <= 0x66)  )  {
			tary[i]	= tch + 10 - 0x61;
		} else {
			/* range error */
			return -2;
		}
	}

	/* check crc16 .. */
	crc = 0;
	for ( int i=0; i<7; i++ )  {
		tary[i] = (tary[i*2] << 4) | tary[i*2 + 1];
		crc = update_crc16( tary[i], crc );
	}

	if ( crc != 0 )  {
		return -1;
	}

	/* todo : other tail ??? */

	/**/
	*ptype = tary[0];
	temp = tary[4];
	temp = (temp << 8) | tary[3];
	temp = (temp << 8) | tary[2];
	temp = (temp << 8) | tary[1];
	*pargs = temp;

	/* 
	尽量消除末尾的 0x0D, 0x0A, 或者 0x8D, 0x8A. 
	还有 XON, XOFF 也尽量消除.

	如果 hex_header 后面还是 hex_header,  那么因为 hex_header 的 PAD, ZDEL 过滤机制, 
	会自动过滤消除掉若干 0x0D, 0x0A, 0x11 .

	但是 hex_header 之后是个 data subpacket,  比如 ZFILE header 后面这种情况.
	就不是很好处理.

	1) 或者永远认为 hex_header 之后, 必须要有 0x0D, 0x0A, 0x11 这三个字符.
	2) 或者认为可有可无, ...
	*/

	while ( true )  {

		iret = drv_rx_raw( &tch, chtmo );
		if ( iret != 0 )  {
			/* timeout */
			return 0;
		}

		if ( tch == ASCII_XON )  {
			return 0;
		}

		if ( (tch == 0x0A) || (tch == 0x8A) )  {
			continue;
		}

		if ( (tch == 0x0D) || (tch == 0x8D) )  {
			continue;
		}

		/* 以上都不匹配, 报告错误, 或者支持 字符回退给 drv. */
		return 2333;
	}
}



typedef struct _tag_zmod_cfg {
	
	uint32_t pkt_tmo;	/* pkt 接收第一个有效字节的 timeout 值,  单位 us .*/
	uint32_t chr_tmo;	/* 后续字节的 timeout 值, 单位 us , 通常 5 个 char 的 uart 传输时间. */

} zmod_cfg_t;




/*
@brief
	跳过不需要的控制字符, 
	如果 ticks 间隔没有收到任何数据, 返回超时.
	如果一直收到字符, 但是就没有期望的 frame ???
	检查连续 CAN 字符, 5 个连续表示用户干预退出...
@param
@retval
	0 : 正确收到一种 frame , CRC 检验通过.
	111 : 连续收到 5 个 CAN/ZDLE (0x18) 字符.
	123 : 超时.

	-1 : crc 检查错误???
	-2 : hex 字符范围不正确.
*/

int zmod_recv_any_header( zmod_cfg_t * pcfg, uint8_t * ptype, uint32_t * pargs )
{
	int iret;
	uint8_t tch;
	uint8_t tary[4];

	/*
	尝试收第一个字节数据, 只有这次的接收, 按照超时时间考虑是否超时. 
	后续数据接收按照 115200 波特率的字符 86us 间隔设置, 放大一下考虑 200us.
	*/
	tary[0] = 0;
	tary[1] = 0;
	while ( true )  {
		
		iret = drv_rx_raw( &tch, pcfg->pkt_tmo );
		if ( iret != 0 )  {
			/* time out */
			return 1231;
		}

		// printf( "R:%X\n", tch );

		if ( tch == 0x11 )  {
			continue;
		}

		if ( (tary[1] == ZDLE) &&  (tch == ZDLE) )  {
			/* 有可能是 CAN, CAN ... */
			return 1232;
		}

		if ( (tary[0] == ZPAD) && (tary[1] == ZDLE) ){

			if ( tch == ZBIN )  {

				return recv_bin_frame( pcfg->chr_tmo, ptype, pargs );

			} else if ( tch == ZHEX )   {
				
				return recv_hex_frame( pcfg->chr_tmo, ptype, pargs );

			} else {

				return (33000 + tch);
			}

		}

		/* shift */
		tary[0] = tary[1];
		tary[1] = tch;
	}

}


static uint32_t utils_flags( uint8_t zf0, uint8_t zf1, uint8_t zf2, uint8_t zf3 )
{
	uint32_t temp;

	temp = zf0;
	temp = (temp << 8) | zf1;
	temp = (temp << 8) | zf2;
	temp = (temp << 8) | zf3;

	return temp;
}


/*
@brief
	第一步, ZRQINIT, ZRINIT.
	结果是获得接收方的 能力信息.
@param
@retval
	0 - succ
	123 - time out
*/
static int zmod_tx_init( zmod_cfg_t * pcfg )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;

	/**/
	send_hex_frame( ZRQINIT, 0 );

	iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
	if ( 0 != iret )  {
		return iret;
	}

	if ( ftype != ZRINIT )  {
		return -1000;
	}

	/* result */
	return 0;
}


#define ZF0_ZCBIN		1		/* Binary transfer - inhibit conversion */
#define ZF1_ZMCHNG		8		/* Change filename if destination exists */
#define ZF2_ZTNOR		0		/* no compression */

static int zmod_tx_header( zmod_cfg_t * pcfg, char * fname, uint32_t fsize )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;
	char  tinfo[64];

	/**/
	fargs = utils_flags( ZF0_ZCBIN, ZF1_ZMCHNG, ZF2_ZTNOR, 0 );
	send_bin16_frame( ZFILE, fargs );

	/**/
	iret = strlen( fname ) + 1;
	memcpy( tinfo, fname, iret );
	iret = iret + 1;
	// iret = sprintf( tinfo, "%s\0%lu\0", fname, fsize );
	send_data_subpacket( ZCRCW, iret, (uint8_t *)tinfo );

	/* wait recv */
	iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
	if ( 0 != iret )  {
		return iret;
	}

	if ( ftype == ZRPOS )  {
		//printf( "ZRPOS %lx\n", fargs );
		return 0;
	}

	/**/
	// printf( "%x, %lx\n", ftype, fargs );
	return -2000;
}


static int zmod_tx_data( zmod_cfg_t * pcfg, uint32_t offs, int tlen, uint8_t * pdat )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;

	/**/
	send_bin16_frame( ZDATA, offs );

	for ( int i=0; i<tlen; i+=256 )  {
		
		/* nostop */
		send_data_subpacket( ZCRCG, 256, pdat + i );

		/* try check error,  but short timeout */
		iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
		if ( iret > 1230 )  {
			printf( "data tmo, %u\n", iret );
			continue;
		}

		if ( iret != 0 )  {
			return iret;
		}

		if ( ftype == ZRPOS )  {
			/* 根据 offset 重传... */
			printf( "ZRPOS, retry trans, %lx\n", fargs );
		}

	}

	/* 最后一片数据? 特殊的, 需要等待 ACK??? */
	send_data_subpacket( ZCRCE, 0, NULL );
	return 0;

}


/*
@brief
	发送 EOF 表示文件结束了,  期望收到 ZRINIT.
	其他情况??
@param
@retval
*/
static int zmod_tx_ending( zmod_cfg_t * pcfg, uint32_t tlen )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;

	/**/
	send_bin16_frame( ZEOF, tlen );

	iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
	if ( iret != 0 )  {
		return iret;
	}

	if ( ftype != ZRINIT )  {
		printf( "ending, %x, %lx\n", ftype, fargs );
		return -30;
	}

	return 0;
}


/*
@brief
	发送 ZFIN, 期望收到回应 ZFIN.
	如果正常, 再发送一个 OO .
@param
@retval
*/
static int zmod_tx_finish( zmod_cfg_t * pcfg )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;

	/**/
	send_bin16_frame( ZFIN, 0 );

	iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
	if ( iret != 0 )  {
		return iret;
	}

	if ( ftype != ZFIN )  {
		return -30;
	}

	/* send OO */
	drv_tx_raw( 0x4F );
	drv_tx_raw( 0x4F );
	return 0;
}



int zmodem_cmd_send( void * pctx, int argc, const char **argv )
{
	int iret;
	zmod_cfg_t  tcfg;

	/**/
	tcfg.pkt_tmo = 1000000;
	tcfg.chr_tmo = 800;

	/**/	
	iret = zmod_tx_init( &tcfg );
	if ( iret != 0 )  {
		printf( "tx init, fail, %d\n", iret );
		goto trx_abort;
	}

	iret = zmod_tx_header( &tcfg, "data-1.bin", 1024 );
	if ( iret != 0 )  {
		printf( "tx header, fail, %d\n", iret );
		goto trx_abort;
	}

	iret = zmod_tx_data( &tcfg, 0, 1024, (uint8_t *)(intptr_t)0x10001000 );
	if ( iret != 0 )  {
		printf( "tx data, fail, %d\n", iret );
		goto trx_abort;
	}

	iret = zmod_tx_ending( &tcfg, 1024 );
	if ( iret != 0 )  {
		printf( "tx ending, fail, %d\n", iret );
		goto trx_abort;
	}

	iret = zmod_tx_finish( &tcfg );
	if ( iret != 0 )  {
		printf( "tx finish, fail, %d\n", iret );
		goto trx_abort;
	}

	/* nothing */
	printf( "tx all, succ\n" );
	return 0;

	/**/
trx_abort:
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	return 0;
}



/*
调用者提供 buffer 空间,  最长 1024, 
考虑 timeout 
*/
int zmod_recv_subpacket( uint8_t * ptype, int * plen, uint8_t * pdat, uint32_t tmous )
{
	int iret;
	int offs;
	uint16_t crc;
	uint8_t tch;

	/* first char, 设置长一些的超时时间.. */
	iret = drv_recv_escap( &tch, tmous );
	if ( iret < 0 )  {
		return __LINE__;
	}

	if ( iret != 0 )  {
		/* todo : zero length subpacket */
		printf( "abnormal escape : %x\n", tch );
		return 1234;
	}

	/**/
	pdat[0] = tch;
	offs = 1;
	crc = update_crc16( tch, 0 );

	while ( true )  {

		iret = drv_recv_escap( &tch, 800 );
		if ( iret < 0 )  {
			return __LINE__;
		}

		if ( iret == 0 )  {

			if ( offs >= 1024 ) {
				return 1238;
			}

			pdat[offs] = tch;
			offs += 1;
			crc = update_crc16( tch, crc );
			continue;
		}

		/* frame ending ? ZCRCE, ZCRCG, ZCRCQ, ZCRCW */
		if ( (tch == ZCRCE) || (tch == ZCRCG) || (tch == ZCRCQ) || (tch == ZCRCW) )   {
			
			crc = update_crc16( tch, crc );
			
			/**/
			*ptype = tch;
			*plen = offs;
			break;
		}

		/* error .. */
		return 1239;
	}

	/* crc recv .. */
	iret = drv_recv_escap( &tch, tmous );
	if ( iret != 0 )  {
		return 1235;
	}

	crc = update_crc16( tch, crc );

	iret = drv_recv_escap( &tch, tmous );
	if ( iret != 0 )  {
		return 1236;
	}

	crc = update_crc16( tch, crc );
	if ( crc != 0 )  {
		printf( "crc : %x\n", crc );
		return __LINE__;
	}

	/**/
	return 0;
}




#define	CANFDX		0x01	/* Rx can send and receive true full duplex */
#define CANOVIO		0x02	/* Rx can receive data during disk I/O */
#define CANBRK		0x04	/* Rx can send a break signal */
#define CANCRY		0x08	/* Receiver can decrypt */
#define CANLZW		0x10	/* Receiver can uncompress */
#define CANFC32		0x20	/* Receiver can use 32 bit Frame Check */



/*
@brief
	第一步, ZRINIT.
	结果是获得接收方的 能力信息.
@param
@retval
	0 - succ
	123 - time out
*/
static int zmod_recv_init( zmod_cfg_t * pcfg )
{
	int i;
	int iret;
	int tlen;
	uint8_t ftype;
	uint32_t fargs;
	uint8_t tary[1024];

	for ( i=0; i<10; i++ )  {

		/**/
		fargs = utils_flags( CANFDX | CANOVIO, 0, 0, 0 );
		send_hex_frame( ZRINIT, fargs );

		/**/
		iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
		if ( iret == 0 )  {
			break;
		}

		if ( iret == 1231 )  {
			continue;
		} else {
			return 1440;
		}
	}

	if ( i >= 10 )  {
		return 1444;
	}

	if ( ftype != ZFILE )  {
		printf( ":: %X, %lX\n", ftype, fargs );
		return 3000;
	}

	/* todo : fargs check.. */

	/*
	ZFILE header followed by a ZCRCW data subpacket containing 
	the file name, file length and modification date.
	file name 之后使用 '\0' 结尾.
	fiel length 和 mod data 都是字符串, 它们之间使用 0x20 作为分隔.
	*/

	/**/
	iret = zmod_recv_subpacket( &ftype, &tlen, tary, 30000 );
	if ( iret != 0 )  {
		printf( "file subpkt : %d\n", iret );
		return 4001;
	}

	/* dump info */
	if ( ftype != ZCRCW )  {
		return 4002;
	}

	return 0;
}



/*
期望 ZDATA ( offs=0 )
如果数据正确,  保存到指定位置. 同时记录 offset .
一直处理到 frame end, 返回 frame end type.
*/
int zmod_recv_data( zmod_cfg_t * pcfg, uint8_t * tgmem, uint32_t * plen )
{
	int iret;
	int tlen;
	uint8_t ftype;
	uint32_t fargs;
	uint32_t filepos;
	uint8_t tary[1024];

	/* send ZRPOS(0) */
	filepos = 0;
	send_hex_frame( ZRPOS, 0 );

	/* recv ZDATA */
	while ( true )  {
		
		iret = zmod_recv_any_header( pcfg, &ftype, &fargs );
		if ( iret != 0 )  {
			printf( "expct DATA, err : %d\n", iret );
			iret = __LINE__;
			break;
		}

		if ( ftype == ZEOF )  {

			/* 发送端表示 到达文件末尾, 是否所有内容都接收完成?? */
			if ( fargs == filepos )  {
				*plen = filepos;
				return 0;
			}

			/* 接收长度不完整, 重新发送 ZPOS,  并且记录 retry 次数. */
			send_hex_frame( ZRPOS, filepos );
			continue;
		}

		if ( ftype != ZDATA )  {
			/* 其他消息?? */
			printf( "expct DATA, type err : %d\n", ftype );
			iret = __LINE__;
			break;
		}

		/* 确认 ZDADA header 里面的 postion 参数是否是我们所需. */
		if ( fargs != filepos )  {
			
			/* 重新发送 ZPOS,  并且记录 retry 次数. */
			send_hex_frame( ZRPOS, filepos );
			continue;
		}

		/* ZDATA 之后是若干 data subpacket*/
		while ( true )  {

			iret = zmod_recv_subpacket( &ftype, &tlen, tary, 2000000 );
			if ( iret != 0 )  {

				/* 超时  或者  CRC错误. */

				/* 如果错误, 需要使用 ZRPOS 通知来重新传输. */

				/* debug print */
				printf( "cvt: %x\n", tlen );
				debug_dump_hex( tary, 1024 );
				iret = __LINE__;
				goto trx_abort;
			}

			/*
			接收到 0 长度的 subpacket 是正常情况,  可能 sender 读磁盘有点耗时.
			A zero length data frame may be used as an idle subpacket 
			to prevent the receiver from timing out in case 
			data is not immediately available to the sender.
			*/
			if ( tlen != 0 )  {

				/*	如果 tary 内容正确, 复制字节数据到 target memory .. */
				memcpy( tgmem + filepos, tary, tlen );
				filepos += tlen;
			}

			// printf( ":%x:%d\n", ftype, tlen );

			/*
			ZCRCE, ZCRCW : 表示 frame end, 后续是个 header packet 需要接收.
			并且 ZCRCW 表示需要 一个 ZACK 表示数据接收正确.

			ZCRCG, ZCRCQ : 表示后续还是若干个 subpacket.
			并且 ZCRCQ 表示需要 一个 ZACK 表示数据接收正确.
			*/

			if ( (ftype == ZCRCQ) || (ftype == ZCRCW) )  {
				
				/* ZACK */
				send_hex_frame(  ZACK, filepos );
				printf( "A:%lx\n", filepos );
			}

			if ( (ftype == ZCRCE) || (ftype == ZCRCW) )  {
				/* 跳出 subpacket 循环后,  重新接收 header . */
				break;
			}
		}
	}

trx_abort:
	/* error .. */
	return iret;
}



/*
@brief
	zmodem 协议.

@param
	pdat 指定接收数据保存的内存地址.
	maxs 指定空间 能容纳的 最大字节长度.
	plen 如果传输过程没有问题, 这里返回接收到的数据长度. 

@return
	0 表示成功, 其他表示错误码.

@todo
	在接收过程中检查 maxs 超过最大长度.
	检查到错误之后, 重传???
*/
int zmodem_cmd_recv( void * pdat, int maxs, int * plen )
{
	int iret;
	uint8_t ftype;
	uint32_t fargs;
	uint32_t tlen;
	zmod_cfg_t  tcfg;

	/**/
	tcfg.pkt_tmo = 1000000;
	tcfg.chr_tmo = 800;

	/**/
	drv_rx_purge();
	iret = zmod_recv_init( &tcfg );
	if ( iret != 0 )  {
		printf( "RX init, fail, %d\n", iret );
		goto trx_abort;
	}


	iret = zmod_recv_data( &tcfg, (uint8_t *)(uintptr_t)pdat, &tlen );
	if ( iret != 0 )  {
		goto trx_abort;
	}


	/* 接收过程正常结束, 收尾过程.. */
	fargs = utils_flags( CANFDX | CANOVIO, 0, 0, 0 );
	send_hex_frame( ZRINIT, fargs );

	while ( true )  {

		iret = zmod_recv_any_header( &tcfg, &ftype, &fargs );
		if ( iret != 0 )  {
			goto trx_abort;
		}

		/**/
		if ( ftype == ZFILE )  {
			send_hex_frame( ZSKIP, 0 );
			continue;
		}

		if ( ftype == ZFIN )  {
			send_hex_frame( ZFIN, 0 );
			break;
		}

		/* todo : other???  */
		goto trx_abort;

	}

	/**/
	*plen = (int)tlen;
	return 0;

trx_abort:
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	drv_tx_raw( ASCII_CAN );
	return 123;
}


